﻿using System;
using System.Linq;
using System.Net;
using WebsiteLoadTool.Configuration;

namespace WebsiteLoadTool.Worker
{
    public static class WebsiteLoader
    {
        public static void Load(Website website, Action<string> writeLogEntry)
        {
            WebsiteLoadResult result = new WebsiteLoadResult()
            {
                ID = Guid.NewGuid(),
                StartDateTime = DateTime.Now,
                Status = ResultStatusEnum.InProgress
            };

            website.Results.Insert(0, result);

            int maxResultLength = int.Parse(ConfigSettingHelper.ReadSetting("MaxResultLength"));

            if (website.Results.Count > maxResultLength)
            {
                while (website.Results.Count > maxResultLength)
                    website.Results.RemoveAt(website.Results.Count - 1);
            }

            Observable.Start<byte[]>(
                new Func<byte[]>(
                    () =>
                    {
                        byte[] data = null;

                        Uri path = null;
                        if (Uri.TryCreate(website.URI, UriKind.RelativeOrAbsolute, out path))
                        {
                            WebClient client = new WebClient();

                            if (!string.IsNullOrWhiteSpace(website.Username) && !string.IsNullOrWhiteSpace(website.Password) && !string.IsNullOrWhiteSpace(website.Domain))
                                client.Credentials = new NetworkCredential(website.Username, website.Password, website.Domain);
                            else if (!string.IsNullOrWhiteSpace(website.Username) && !string.IsNullOrWhiteSpace(website.Password))
                                client.Credentials = new NetworkCredential(website.Username, website.Password);

                            data = client.DownloadData(path);
                        }

                        return data;

                    }))
                    .SubscribeOnDispatcher()
                    .Finally(
                    () =>
                    {
                        result.Duration = DateTime.Now.Subtract(result.StartDateTime);

                        var beforeLastResult = (from r in website.Results
                                                where !WebsiteLoadResult.Equals(r, website.LastResult)
                                                orderby r.StartDateTime descending
                                                select r).FirstOrDefault();

                        if (beforeLastResult == null)
                        {
                            string logEntry = string.Format("Website {0} ({1}) recieved state '{2}' after {3:c}. {4}", website.Name, website.URI, result.Status, result.Duration, result.Message);
                            writeLogEntry(logEntry);
                        }
                        else if (!Enum.Equals(website.LastResult.Status, beforeLastResult.Status))
                        {
                            string logEntry = string.Format("Website {0} ({1}) changed state to '{2}' after {3:c}. {4}", website.Name, website.URI, result.Status, result.Duration, result.Message);
                            writeLogEntry(logEntry);
                        }

                        DataFileHelper.WriteWebsiteResults(website);
                    })
                    .Subscribe(
                    (data) =>
                    {
                        if (data == null || data.Length == 0)
                        {
                            result.Status = ResultStatusEnum.Failed;
                        }
                        else
                        {
                            result.Status = ResultStatusEnum.Success;
                            result.EndDateTime = DateTime.Now;
                            result.Message = string.Format("Succesfully downloaded {0:N0} bytes", data.Length);
                        }
                    },
                    (exception) =>
                    {
                        if (exception is WebException)
                        {
                            WebException wex = (WebException)exception;

                            if (wex.Status == WebExceptionStatus.Timeout)
                                result.Status = ResultStatusEnum.TimedOut;
                            else
                                result.Status = ResultStatusEnum.Failed;

                            result.Message = string.Format("{0}; status: {1}", wex.Message, wex.Status);
                        }
                        else
                        {
                            result.Status = ResultStatusEnum.Failed;
                            result.Message = exception.Message;
                        }
                    });
        }
    }
}
